﻿using CommonModel.Util;
using GXRTBTC.PublicClassLibrary.Model;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace GXRTBTC.PublicClassLibrary.Util
{
    /// <summary>
    /// 生成下发命令
    /// </summary>
    public static class CommandUtil
    {
        /// <summary>
        /// 生成下发命令
        /// </summary>
        /// <param name="operateModel"></param>
        public static OperateModel GetCommand(OperateModel operateModel, ILogger mLogger = null)
        {
            string errmsg;
            operateModel.cmd = operateModel.cmd_temp;

            try
            {
                //循环替换各个动态参数
                //int n = 0;
                List<string> paras = operateModel.cmd_paras.Split(',').ToList();
                for (int n = 0; n < paras.Count; n++)
                {
                    errmsg = "";
                    string para = paras[n];
                    string hexValue = "";
                    if (para.Contains('"'))
                    {
                        hexValue = para.Replace("\"", "");
                    }
                    else if (TransForm.IsInt(para))
                    {
                        //paraDetails包含有参数
                        if (operateModel.paraDetails.ContainsKey(para))
                        {
                            Dictionary<string, string> paraDetail = operateModel.paraDetails[para].ToDictionary(d => d.Key, d => d.Value.ToString());
                            string para_code = paraDetail["para_code"].ToString().ToUpper();
                            string para_type = paraDetail["para_type"].ToString().ToUpper();
                            int para_length = TransForm.IsInt(paraDetail["para_length"].ToString()) ? int.Parse(paraDetail["para_length"].ToString()) : 1;
                            bool from_svr = bool.Parse(paraDetail["from_svr"].ToString());
                            int calc_type = int.Parse(paraDetail["calc_type"].ToString());
                            string para_value = (calc_type == 0 && operateModel.parasValues.ContainsKey(para) ? operateModel.parasValues[para] : "");

                            string source_code = paraDetail.ContainsKey("source_code") ? paraDetail["source_code"].ToString() ?? "" : "";
                            string rParaKey = para + "-" + source_code;

                            //有些参数，数据需要来自于后台
                            if (from_svr)
                            {
                                //台站编号
                                if (para_code.Equals("STATION_ID"))
                                {
                                    para_value = operateModel.deviceInfo.stationCode;
                                }
                                //设备地址
                                else if (para_code.Equals("DEV_ADDR"))
                                {
                                    if (TransForm.IsInt(operateModel.deviceInfo.dev_addr))
                                    {
                                        para_value = operateModel.deviceInfo.dev_addr;
                                    }
                                    else
                                    {
                                        para_value = "0";
                                    }
                                }
                                //实时时间 远程监控协议 CmdTime[RT_STR_LEN] 实时时间字符串长度，格式：“时:分:秒.毫秒” 即“HH:mm:ss.fff”
                                else if (para_code.Equals("SYSTIME"))
                                {
                                    para_value = TransForm.GetFormatDatetime("HH:mm:ss.fff");
                                }
                                //当前时间
                                else if (para_code.Equals("NOW"))
                                {
                                    para_value = TransForm.DateTimeFormat(DateTime.Now);
                                }
                                //特殊参数，为OperateModel类的oper_key参数
                                else if (para_code.Equals("OPER_KEY"))
                                {
                                    para_value = operateModel.oper_key;
                                }
                            }

                            //特殊信息特殊处理
                            //广西广播电视设备通用通信协议crc16校验，固定para_code
                            //crc16校验，除去帧头以及crc本身2字节外的帧体进行校验
                            //calc_type=1固定为通用协议crc计算，不用para_code去固定
                            //if ("CRC16".Equals(para_code.ToUpper()))
                            if (calc_type == 1)
                            {
                                //第41字节开始，最后一个参数才是crc16的标识“{n}”，定位到{即可
                                byte[] bodyCmd = TransForm.HexTobyte(operateModel.cmd.Substring(40, operateModel.cmd.LastIndexOf('{') - 40));
                                hexValue = TransForm.byteToHex(TransForm.IntToByteArray(TransForm.CRC_16(bodyCmd), true, false));
                            }
                            //研发部自研协议crc校验，固定calc_type为2，校验帧体，其中帧头18
                            else if (calc_type == 2)
                            {
                                //第37字节开始，最后一个参数才是crc16的标识“{n}”，定位到{即可
                                byte[] bodyCmd = TransForm.HexTobyte(operateModel.cmd[36..operateModel.cmd.LastIndexOf('{')]);
                                hexValue = TransForm.byteToHex(TransForm.IntToByteArray(TransForm.CRC_16(bodyCmd), true, false));
                            }
                            //广西广播电视设备通用通信协议，数据长度计算
                            //“数据长度”字段后，“校验”字段前，参数数据的总字节数。类型：UInt16。长度：2字节
                            else if (calc_type == 3)
                            {
                                //取出当前命令，保存临时命令
                                string tmpCmd = operateModel.cmd;
                                //临时命令，根据参数长度，将动态参数用0填充临时命令，然后截取临时命令，获得长度
                                for (int i = n; i < paras.Count; i++)
                                {
                                    string tmpPara = paras[i];
                                    if (tmpPara.Contains('"'))
                                    {
                                        tmpCmd = tmpCmd.Replace("{" + i + "}", tmpPara.Replace("\"", ""));
                                    }
                                    else
                                    {
                                        Dictionary<string, string> paraDetailIn = operateModel.paraDetails[tmpPara].ToDictionary(d => d.Key, d => d.Value.ToString());
                                        int tmpLen = TransForm.IsInt(paraDetailIn["para_length"].ToString()) ? int.Parse(paraDetailIn["para_length"].ToString()) : 1;
                                        tmpCmd = tmpCmd.Replace("{" + i + "}", "".PadRight(tmpLen * 2, '0'));
                                    }
                                }
                                //长度固定就是int类型
                                hexValue = tmpCmd.Length > 48 ? TransForm.byteToHex(TransForm.IntToByteArray((tmpCmd[48..].Length - 4) / 2, true, false)) : "00";
                            }
                            //帧体总长-远程监控协议 帧头长度8
                            //帧体总长——除本帧头外，帧体字节数。类型：UInt16。长度：2字节
                            else if (calc_type == 4)
                            {
                                //取出当前命令，保存临时命令
                                string tmpCmd = operateModel.cmd;
                                //临时命令，根据参数长度，将动态参数用0填充临时命令，然后截取临时命令，获得长度
                                for (int i = n; i < paras.Count; i++)
                                {
                                    string tmpPara = paras[i];
                                    if (tmpPara.Contains('"'))
                                    {
                                        tmpCmd = tmpCmd.Replace("{" + i + "}", tmpPara.Replace("\"", ""));
                                    }
                                    else
                                    {
                                        Dictionary<string, string> paraDetailIn = operateModel.paraDetails[tmpPara].ToDictionary(d => d.Key, d => d.Value.ToString());
                                        int tmpLen = TransForm.IsInt(paraDetailIn["para_length"].ToString()) ? int.Parse(paraDetailIn["para_length"].ToString()) : 1;
                                        tmpCmd = tmpCmd.Replace("{" + i + "}", "".PadRight(tmpLen * 2, '0'));
                                    }
                                }
                                //长度固定就是int类型
                                hexValue = tmpCmd.Length > 16 ? TransForm.byteToHex(TransForm.IntToByteArray(tmpCmd[16..].Length / 2, false, false)) : "00";
                            }
                            //广西广播电视设备通用通信协议帧体，通过source_code关联到设置表，获取参数，多个code+len+val集合 其中len=1
                            else if (calc_type == 5)
                            {
                                byte[] tmpBytes = new byte[0];
                                int lenCalc = 0;
                                //计算集合
                                if (operateModel.paraDetails.ContainsKey(rParaKey))
                                {
                                    //唯一参数，记录总长度
                                    Dictionary<string, int> paraNames = new();
                                    //定位需要修改长度值的位置
                                    int lenModify = tmpBytes.Length;
                                    //数据集
                                    //Dictionary<string, Dictionary<string, string>> modelParaInfos = operateModel.paraDetails[rParaKey].ToDictionary(d => d.Key, d => JsonConvert.DeserializeObject<Dictionary<string, string>>(d.Value.ToString()));
                                    Dictionary<string, Dictionary<string, string>> modelParaInfos = operateModel.paraDetails[rParaKey].ToDictionary(d => d.Key, d => (Dictionary<string, string>)d.Value);
                                    foreach (string para_name in modelParaInfos.Keys)
                                    {
                                        Dictionary<string, string> modelParaInfo = modelParaInfos[para_name];
                                        //para_name和paraNameIn不一样，paraNameIn是真实的，para_name可能是修正后带自增编号的
                                        string paraNameIn = modelParaInfo["para_name"].ToUpper();
                                        string paraCode = modelParaInfo["para_code"].ToUpper();
                                        string paraType = modelParaInfo["para_type"].ToUpper();
                                        string value = operateModel.parasValues.ContainsKey(para_name) ? operateModel.parasValues[para_name] : "";
                                        int paraLength = TransForm.IsInt(modelParaInfo["para_length"]) ? int.Parse(modelParaInfo["para_length"]) : 1;
                                        //通用协议时间表设置时，长度直接取自值
                                        if ("TIMEARRAY".Equals(paraType))
                                        {
                                            paraLength = value.Length / 2;
                                        }

                                        //数据编号2 + 数据长度1 + 数据长度para_length
                                        byte[] oneBytes;
                                        byte[] btLen;
                                        byte[] btVal = "".Equals(value) ? new byte[0] : TransForm.HexTobyte(GetHexValue(paraType, value, out errmsg), false);

                                        //唯一参数
                                        if (paraNames.ContainsKey(paraNameIn))
                                        {
                                            //paraNameIn重复新增的，说明是带自增编号的，在通用协议下，需要修改长度和且值连接在已新增的后面
                                            paraNames[paraNameIn] = paraNames[paraNameIn] + paraLength;

                                            //这里只需要将值连接在后面，所以长度就是paraLength
                                            oneBytes = new byte[paraLength];
                                            //这里长度修改为总长度
                                            btLen = TransForm.IntToByteArray(paraNames[paraNameIn], true, false);
                                            //修改tmpBytes里，paraCode对应的长度值
                                            Buffer.BlockCopy(btLen, 0, tmpBytes, lenModify, btLen.Length < paraLength ? btLen.Length : paraLength);
                                            //连接值
                                            Buffer.BlockCopy(btVal, 0, oneBytes, 0, btVal.Length < paraLength ? btVal.Length : paraLength);

                                            lenCalc += paraLength;
                                        }
                                        else
                                        {
                                            //记录位置，当前长度加上2个code的位置
                                            lenModify = tmpBytes.Length + 2;
                                            paraNames.TryAdd(paraNameIn, paraLength);

                                            //数据编号2 + 数据长度1 + 数据长度para_length
                                            oneBytes = new byte[2 + 1 + paraLength];
                                            //默认长度为获取的长度
                                            btLen = TransForm.IntToByteArray(paraLength, true, false);

                                            //拼接
                                            Buffer.BlockCopy(TransForm.HexTobyte(paraCode, true), 0, oneBytes, 0, 2);
                                            Buffer.BlockCopy(btLen, 0, oneBytes, 2, 1);
                                            Buffer.BlockCopy(btVal, 0, oneBytes, 3, btVal.Length < paraLength ? btVal.Length : paraLength);

                                            lenCalc += 2 + 1 + paraLength;
                                        }

                                        tmpBytes = tmpBytes.AddBytes(oneBytes);
                                    }
                                }
                                para_length = lenCalc;
                                hexValue = TransForm.byteToHex(tmpBytes);
                            }
                            //其他配置协议，没有para_code,通过source_code关联到设置表，获取参数，多个val集合
                            else if (calc_type == 6)
                            {
                                hexValue = "";
                                int lenCalc = 0;
                                //计算集合
                                if (operateModel.paraDetails.ContainsKey(rParaKey))
                                {
                                    //Dictionary<string, Dictionary<string, string>> modelParaInfos = operateModel.paraDetails[rParaKey].ToDictionary(d => d.Key, d => JsonConvert.DeserializeObject<Dictionary<string, string>>(d.Value.ToString()));
                                    Dictionary<string, Dictionary<string, string>> modelParaInfos = operateModel.paraDetails[rParaKey].ToDictionary(d => d.Key, d => (Dictionary<string, string>)d.Value);
                                    foreach (string para_name in modelParaInfos.Keys)
                                    {
                                        Dictionary<string, string> modelParaInfo = modelParaInfos[para_name];
                                        //string paraName = modelParaInfo["para_name"].ToUpper();
                                        string paraType = modelParaInfo["para_type"].ToUpper();
                                        int paraLength = TransForm.IsInt(modelParaInfo["para_length"]) ? int.Parse(modelParaInfo["para_length"]) : 1;
                                        string value = operateModel.parasValues.ContainsKey(para_name) ? operateModel.parasValues[para_name] : "";
                                        string tmpHex = "".Equals(value) ? "" : GetHexValue(paraType, value, out errmsg);

                                        lenCalc += paraLength;
                                        //补足长度
                                        tmpHex = tmpHex.PadRight(paraLength * 2, '0');
                                        hexValue += tmpHex;
                                    }
                                }
                                para_length = lenCalc;
                            }
                            //广西地面数字电视发射机通信接口协议，通过source_code关联到设置表，获取参数，多个code+len+val集合 其中len=2
                            else if (calc_type == 7)
                            {
                                byte[] tmpBytes = new byte[0];
                                int lenCalc = 0;
                                //计算集合
                                if (operateModel.paraDetails.ContainsKey(rParaKey))
                                {
                                    //唯一参数，记录总长度
                                    Dictionary<string, int> paraNames = new();
                                    //定位需要修改长度值的位置
                                    int lenModify = tmpBytes.Length;
                                    //数据集
                                    //Dictionary<string, Dictionary<string, string>> modelParaInfos = operateModel.paraDetails[rParaKey].ToDictionary(d => d.Key, d => JsonConvert.DeserializeObject<Dictionary<string, string>>(d.Value.ToString()));
                                    Dictionary<string, Dictionary<string, string>> modelParaInfos = operateModel.paraDetails[rParaKey].ToDictionary(d => d.Key, d => (Dictionary<string, string>)d.Value);
                                    foreach (string para_name in modelParaInfos.Keys)
                                    {
                                        Dictionary<string, string> modelParaInfo = modelParaInfos[para_name];
                                        string paraNameIn = modelParaInfo["para_name"].ToUpper();
                                        string paraCode = modelParaInfo["para_code"].ToUpper();
                                        string paraType = modelParaInfo["para_type"].ToUpper();
                                        string value = operateModel.parasValues.ContainsKey(para_name) ? operateModel.parasValues[para_name] : "";
                                        int paraLength = TransForm.IsInt(modelParaInfo["para_length"]) ? int.Parse(modelParaInfo["para_length"]) : 1;
                                        //通用协议时间表设置时，长度直接取自值
                                        if ("TIMEARRAY".Equals(paraType))
                                        {
                                            paraLength = value.Length / 2;
                                        }

                                        //数据编号2 + 数据长度2 + 数据长度para_length
                                        byte[] oneBytes;
                                        byte[] btLen;
                                        byte[] btVal = "".Equals(value) ? new byte[0] : TransForm.HexTobyte(GetHexValue(paraType, value, out errmsg), false);

                                        //唯一参数
                                        if (paraNames.ContainsKey(paraNameIn))
                                        {
                                            //paraNameIn重复新增的，说明是带自增编号的，在通用协议下，需要修改长度和且值连接在已新增的后面
                                            paraNames[paraNameIn] = paraNames[paraNameIn] + paraLength;

                                            //这里只需要将值连接在后面，所以长度就是paraLength
                                            oneBytes = new byte[paraLength];
                                            //这里长度修改为总长度
                                            btLen = TransForm.IntToByteArray(paraNames[paraNameIn], true, false);
                                            //修改tmpBytes里，paraCode对应的长度值
                                            Buffer.BlockCopy(btLen, 0, tmpBytes, lenModify, btLen.Length < paraLength ? btLen.Length : paraLength);
                                            //连接值
                                            Buffer.BlockCopy(btVal, 0, oneBytes, 0, btVal.Length < paraLength ? btVal.Length : paraLength);

                                            lenCalc += paraLength;
                                        }
                                        else
                                        {
                                            //记录位置，当前长度加上2个code的位置
                                            lenModify = tmpBytes.Length + 2;
                                            paraNames.TryAdd(paraNameIn, paraLength);

                                            //数据编号2 + 数据长度1 + 数据长度para_length
                                            oneBytes = new byte[2 + 2 + paraLength];
                                            //默认长度为获取的长度
                                            btLen = TransForm.IntToByteArray(paraLength, true, false);

                                            //拼接
                                            Buffer.BlockCopy(TransForm.HexTobyte(paraCode, true), 0, oneBytes, 0, 2);
                                            Buffer.BlockCopy(btLen, 0, oneBytes, 2, btLen.Length);
                                            Buffer.BlockCopy(btVal, 0, oneBytes, 4, btVal.Length < paraLength ? btVal.Length : paraLength);

                                            lenCalc += 2 + 2 + paraLength;
                                        }

                                        tmpBytes = tmpBytes.AddBytes(oneBytes);
                                    }
                                }
                                para_length = lenCalc;
                                hexValue = TransForm.byteToHex(tmpBytes);
                            }
                            else
                            {
                                hexValue = GetHexValue(para_type, para_value, out errmsg);
                            }

                            //补足长度
                            hexValue = hexValue.PadRight(para_length * 2, '0');
                        }
                    }
                    //替换对应未知参数
                    operateModel.cmd = operateModel.cmd.Replace("{" + n + "}", hexValue);

                    if (!"".Equals(errmsg) && mLogger != null)
                    {
                        mLogger.LogError(errmsg);
                    }
                }
            }
            catch (Exception e)
            {
                if (mLogger != null)
                {
                    mLogger.LogError("命令生成异常，" + e);
                }
            }
            finally
            {
                operateModel.create_time = DateTime.Now;
            }

            return operateModel;
        }

        /// <summary>
        /// 根据类型，返回hex值
        /// </summary>
        /// <param name="para_type"></param>
        /// <param name="para_value"></param>
        /// <returns></returns>
        private static string GetHexValue(string para_type, string para_value, out string errmsg)
        {
            string hexValue;
            errmsg = "";
            try
            {
                //前面流程已经校验过各个类型的数据了，这里不再校验
                switch (para_type.ToUpper())
                {
                    case "UCHAR":
                        //hexValue = TransForm.byteToHex(TransForm.HexTobyte(para_value, false));
                        if (!"".Equals(para_value) && TransForm.IsInt(para_value))
                        {
                            //hexValue = TransForm.byteToHex(TransForm.IntToByteArray(int.Parse(para_value), true, true));
                            hexValue = TransForm.byteToHex(TransForm.IntToByteArray(int.Parse(para_value), true, false));
                        }
                        else
                        {
                            errmsg = "数据转换异常，【" + para_value + "】 不是【" + para_type + "】类型";
                            hexValue = "";
                        }
                        break;
                    case "INT":
                        if (!"".Equals(para_value) && TransForm.IsInt(para_value))
                        {
                            //hexValue = TransForm.byteToHex(TransForm.IntToByteArray(int.Parse(para_value), true, true));
                            hexValue = TransForm.byteToHex(TransForm.IntToByteArray(int.Parse(para_value), true, false));
                        }
                        else
                        {
                            errmsg = "数据转换异常，【" + para_value + "】 不是【" + para_type + "】类型";
                            hexValue = "";
                        }
                        break;
                    case "UINT16":
                        if (!"".Equals(para_value) && TransForm.IsInt(para_value))
                        {
                            //hexValue = TransForm.byteToHex(TransForm.IntToByteArray(int.Parse(para_value), true, true));
                            hexValue = TransForm.byteToHex(TransForm.IntToByteArray(int.Parse(para_value), true, false));
                        }
                        else
                        {
                            errmsg = "数据转换异常，【" + para_value + "】 不是【" + para_type + "】类型";
                            hexValue = "";
                        }
                        break;
                    case "UINT16REF":
                        if (!"".Equals(para_value) && TransForm.IsInt(para_value))
                        {
                            hexValue = TransForm.byteToHex(TransForm.IntToByteArray(int.Parse(para_value), false, false));
                        }
                        else
                        {
                            errmsg = "数据转换异常，【" + para_value + "】 不是【" + para_type + "】类型";
                            hexValue = "";
                        }
                        break;
                    case "UINT8":
                        if (!"".Equals(para_value) && TransForm.IsInt(para_value))
                        {
                            hexValue = TransForm.byteToHex(TransForm.IntToByteArray(int.Parse(para_value), true, false));
                        }
                        else
                        {
                            errmsg = "数据转换异常，【" + para_value + "】 不是【" + para_type + "】类型";
                            hexValue = "";
                        }
                        break;
                    case "BYTE":
                        if (!"".Equals(para_value) && TransForm.IsInt(para_value))
                        {
                            hexValue = TransForm.byteToHex(TransForm.IntToByteArray(int.Parse(para_value), true, false));
                        }
                        else
                        {
                            errmsg = "数据转换异常，【" + para_value + "】 不是【" + para_type + "】类型";
                            hexValue = "";
                        }
                        break;
                    case "INT8":
                        if (!"".Equals(para_value) && TransForm.IsInt(para_value))
                        {
                            hexValue = TransForm.byteToHex(new byte[] { (byte)(int.Parse(para_value)) }, false);
                        }
                        else
                        {
                            errmsg = "数据转换异常，【" + para_value + "】 不是【" + para_type + "】类型";
                            hexValue = "";
                        }
                        break;
                    case "UINT32":
                        if (!"".Equals(para_value) && TransForm.IsInt(para_value))
                        {
                            //hexValue = TransForm.byteToHex(TransForm.IntToByteArray(int.Parse(para_value), true, true));
                            hexValue = TransForm.byteToHex(TransForm.IntToByteArray(int.Parse(para_value), true, false));
                        }
                        else
                        {
                            errmsg = "数据转换异常，【" + para_value + "】 不是【" + para_type + "】类型";
                            hexValue = "";
                        }
                        break;
                    case "IP":
                        hexValue = TransForm.byteToHex(TransForm.FormatIpToBytes(para_value));
                        break;
                    case "FLOAT":
                        hexValue = TransForm.byteToHex(BitConverter.GetBytes(float.Parse(para_value)));
                        break;
                    case "HEX":
                        hexValue = para_value;
                        break;
                    case "STRING":
                        //在使用编码方法（Encoding.GetEncoding("GB2312")）之前，对编码进行注册
                        Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
                        hexValue = TransForm.byteToHex(Encoding.GetEncoding(TransForm.encode).GetBytes(para_value));
                        break;
                    case "ENUM":
                        if (!"".Equals(para_value) && TransForm.IsInt(para_value))
                        {
                            hexValue = TransForm.byteToHex(TransForm.IntToByteArray(int.Parse(para_value), true, false));
                        }
                        else
                        {
                            errmsg = "数据转换异常，【" + para_value + "】 不是【" + para_type + "】类型";
                            hexValue = "";
                        }
                        break;
                    case "TIMEARRAY":
                        //只针对通用协议时间
                        hexValue = para_value;
                        break;
                    default:
                        //在使用编码方法（Encoding.GetEncoding("GB2312")）之前，对编码进行注册
                        Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
                        hexValue = TransForm.byteToHex(Encoding.GetEncoding(TransForm.encode).GetBytes(para_value));
                        break;
                }
            }
            catch (Exception e)
            {
                hexValue = "";
                errmsg = "数据转换异常，para_type=【" + para_type + "】 para_value=【" + para_value + "】 错误信息：" + e;
            }

            return hexValue;
        }
    }
}
